package com.icloud.article.boot.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.icloud.article.boot.dao.DiscussDao;
import com.icloud.article.boot.dao.UpvoteDao;
import com.icloud.article.boot.dto.AdminDiscussPageDto;
import com.icloud.article.boot.dto.DiscussDto;
import com.icloud.article.boot.mapper.DiscussMapper;
import com.icloud.article.boot.mapper.UpvoteMapper;
import com.icloud.article.boot.model.Discuss;
import com.icloud.article.boot.model.DiscussAttachment;
import com.icloud.article.boot.model.Upvote;
import com.icloud.article.boot.query.DiscussQuery;
import com.icloud.article.boot.service.DiscussAttachmentService;
import com.icloud.article.boot.service.DiscussService;
import com.icloud.article.boot.service.UpvoteService;
import com.icloud.article.boot.vo.AdminConsumptionVo;
import com.icloud.article.boot.vo.AdminDiscussVo;
import com.icloud.article.boot.vo.DiscussVo;
import com.icloud.article.boot.vo.StatisticsVo;
import com.icloud.common.core.api.ResultCode;
import com.icloud.common.database.enums.ArticleDiscussScopeEnum;
import com.icloud.common.database.enums.BusinessTypeEnum;
import com.icloud.common.database.enums.DeleteEnum;
import com.icloud.common.database.enums.DiscussSortEnum;
import com.icloud.common.database.mapper.AppCustomerMapper;
import com.icloud.common.database.mapper.ArticleMapper;
import com.icloud.common.database.mapper.AttentionMapper;
import com.icloud.common.database.model.AppCustomer;
import com.icloud.common.database.model.Article;
import com.icloud.common.database.model.Attention;
import com.icloud.common.database.model.TOrders;
import com.icloud.common.web.exception.ApiException;
import com.icloud.common.web.exception.Asserts;
import com.icloud.common.web.util.AuthUtil;
import com.icloud.common.web.vo.PageVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 评论service 实现类
 */
@Service
public class DiscussServiceImpl extends ServiceImpl<DiscussMapper, Discuss> implements DiscussService {
    @Autowired
    private AuthUtil authUtil;
    @Autowired
    private DiscussDao discussDao;
    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private UpvoteService upvoteService;
    @Autowired
    private UpvoteDao upvoteDao;
    @Autowired
    private UpvoteMapper upvoteMapper;
    @Autowired
    private AttentionMapper attentionMapper;
    @Autowired
    private AppCustomerMapper appCustomerMapper;
    @Autowired
    private DiscussAttachmentService discussAttachmentService;

    /**
     * 评论
     * @param dto
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void discuss(DiscussDto dto) {
        Long articleId = dto.getArticleId();
        Article article = articleMapper.selectById(articleId);
        if(article==null){
            throw new ApiException(ResultCode.DATA_NOT_EXISTS);
        }
        Long loginId = authUtil.getUserId();

        String discussion = article.getDiscussion();
        if(ArticleDiscussScopeEnum.isNo(discussion)){
            throw new ApiException(ResultCode.DISCUSSION_NO);
        }
        // 我关注的
        if(ArticleDiscussScopeEnum.isFollowed(discussion)){
            LambdaQueryWrapper<Attention> attQuery = new LambdaQueryWrapper<>();
            attQuery.eq(Attention::getCreatedId, loginId);
            Attention att = attentionMapper.selectOne(attQuery);
            if(att==null){
                throw new ApiException(ResultCode.FOLLOWED_CAN_COMMENT);
            }
        }
        // 关注我的
        if(ArticleDiscussScopeEnum.isFollowMe(discussion)){
            LambdaQueryWrapper<Attention> attQuery = new LambdaQueryWrapper<>();
            attQuery.eq(Attention::getFollowedId, loginId);
            Attention att = attentionMapper.selectOne(attQuery);
            if(att==null){
                throw new ApiException(ResultCode.FOLLOW_ME_CAN_COMMENT);
            }
        }
        if(ArticleDiscussScopeEnum.isAll(discussion)){
        }
        Discuss discuss = new Discuss();
        BeanUtil.copyProperties(dto,discuss);
        discuss.setCreatedId(loginId);
        save(discuss);

        discussAttachmentService.batchAdd(discuss.getId(), dto.getAttachmentList());
    }


    /**
     * 根据帖子id查询评论数量
     * @param articleIds
     * @return
     */
    @Override
    public Map<Long, Long> queryDiscussCountByArticle(List<Long> articleIds) {
        List<StatisticsVo> statisticsVos = discussDao.queryDiscussCountByArticle(new LambdaQueryWrapper<Discuss>()
                .in(Discuss::getArticleId, articleIds).eq(Discuss::getIsDelete, DeleteEnum.NORMAL.getCode())
                .groupBy(Discuss::getArticleId));
        if (CollectionUtil.isEmpty(statisticsVos)){
            return new HashMap<>();
        }
        return statisticsVos.stream().collect(Collectors.toMap(StatisticsVo::getArticleId, c -> c.getCount()));
    }


    /**
     * 根据帖子id查询评论数量
     * @param articleId 帖子id
     * @return
     */
    @Override
    public Long queryDiscussCountByArticle(Long articleId) {
        return count(new LambdaQueryWrapper<Discuss>().eq(Discuss::getArticleId,articleId));
    }


    /**
     * 根据帖子id查询评论列表
     * @param query
     * @return
     */
    @Override
    public List<DiscussVo> list(DiscussQuery query) {

        LambdaQueryWrapper<Discuss> wrapper = new LambdaQueryWrapper<Discuss>()
                .eq(Discuss::getArticleId, query.getArticleId())
                .isNull(Discuss::getParentId);
        if(DiscussSortEnum.TIME.name().equals(query.getSort())){
            wrapper.orderByDesc(Discuss::getCreatedAt);
        }
        if(DiscussSortEnum.HEAT.name().equals(query.getSort())){
            wrapper.orderByDesc(Discuss::getUpvoteCount);
        }

        IPage<DiscussVo> page = discussDao.page(new Page<>(query.getCurrent(), query.getPageSize()), wrapper);
        List<DiscussVo> list = page.getRecords();
        if(CollectionUtil.isEmpty(list)){
            return new ArrayList<>();
        }

        List<Long> discussIds = list.stream().map(DiscussVo::getId).collect(Collectors.toList());

        //查询是否点赞
        Map<Long, Boolean> queryUpvote = upvoteService.queryUpvote(BusinessTypeEnum.DISCUSS.getCode(), discussIds);

        list.forEach(item ->{
            item.setIsUpvote(queryUpvote.get(item.getId()));
            recursion(item);
        });
        return list;
    }

    /**
     * 递归向下查询评论
     * @param vo
     */
    private void recursion(DiscussVo vo){
        List<DiscussVo> list = discussDao.list(new LambdaQueryWrapper<Discuss>()
                .eq(Discuss::getParentId, vo.getId())
                .orderByDesc(Discuss::getCreatedAt));
        if(CollectionUtil.isEmpty(list)){
            return;
        }
        vo.setChild(list);

        List<Long> discussIds = list.stream().map(DiscussVo::getId).collect(Collectors.toList());

        //查询是否点赞
        Map<Long, Boolean> queryUpvote = upvoteService.queryUpvote(BusinessTypeEnum.DISCUSS.getCode(), discussIds);

        list.forEach(item ->{
            item.setIsUpvote(queryUpvote.get(item.getId()));
            recursion(item);
        });
    }


    /**
     * 删除评论
     * @param id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void delete(Long id) {
        Discuss discuss = getById(id);
        Asserts.fail(null == discuss, ResultCode.DATA_NOT_EXISTS);

        Article article = articleMapper.selectById(discuss.getArticleId());
        Asserts.fail(null == article,ResultCode.DATA_NOT_EXISTS);

        Long loginId = authUtil.getUserId();
        //既不是评论人也不是作者无法删除
        Asserts.fail(!(discuss.getCreatedId().equals(loginId)
                || article.getCreatedId().equals(loginId)),ResultCode.FORBIDDEN);

        // 删除评论
        baseMapper.deleteById(discuss);

        // 删除评论的点赞
        LambdaQueryWrapper<Upvote> upvoteQuery = new LambdaQueryWrapper<>();
        upvoteQuery.eq(Upvote::getBusinessType, BusinessTypeEnum.DISCUSS.getCode());
        upvoteQuery.eq(Upvote::getBusinessId, discuss.getId());
        List<Upvote> upvotes = upvoteMapper.selectList(upvoteQuery);
        if(!CollectionUtil.isEmpty(upvotes)){
            List<Long> upvoteIdList = upvotes.stream().map(Upvote::getId).collect(Collectors.toList());
            upvoteMapper.deleteBatchIds(upvoteIdList);
        }

        // 删除评论下所有子评论
        this.recursionRemove(discuss);
    }

    @Override
    public PageVO<AdminDiscussVo> discussPage(AdminDiscussPageDto dto) {
        PageVO<AdminDiscussVo> pageVO = new PageVO<>();
        Long articleId = dto.getArticleId();
        Article article = articleMapper.selectById(articleId);
        Asserts.fail(null==article, ResultCode.DATA_NOT_EXISTS);

        LambdaQueryWrapper<Discuss> discussQuery = new LambdaQueryWrapper<>();
        discussQuery.eq(Discuss::getArticleId, articleId);
        IPage<Discuss> discussIPage = baseMapper.selectPage(Page.of(dto.getCurrent(), dto.getPageSize()), discussQuery);
        List<Discuss> discussList = discussIPage.getRecords();
        if(CollectionUtil.isEmpty(discussList)){
            return pageVO;
        }
        List<Long> discussIdList = discussList.stream().map(Discuss::getId).collect(Collectors.toList());
        Set<Long> customerIdSet = discussList.stream().map(Discuss::getCreatedId).collect(Collectors.toSet());
        List<AppCustomer> appCustomerList = appCustomerMapper.selectBatchIds(customerIdSet);
        if(CollectionUtil.isEmpty(appCustomerList)){
            return pageVO;
        }
        LambdaQueryWrapper<DiscussAttachment> daQuery = new LambdaQueryWrapper<>();
        daQuery.in(DiscussAttachment::getDiscussId, discussIdList);
        List<DiscussAttachment> daList = discussAttachmentService.list(daQuery);
        Map<Long, List<String>> daMap = daList.stream().collect(Collectors.groupingBy(DiscussAttachment::getDiscussId, Collectors.mapping(DiscussAttachment::getUrl, Collectors.toList())));

        Map<Long, AppCustomer> customerMap = appCustomerList.stream().collect(Collectors.toMap(AppCustomer::getId, Function.identity()));
        List<AdminDiscussVo> adminDiscussVoList = discussList.stream().map(d->{
            Long customerId = d.getCreatedId();
            Long discussId = d.getId();
            AdminDiscussVo adminDiscussVo = new AdminDiscussVo();
            adminDiscussVo.setCustomerId(customerId);
            adminDiscussVo.setCreatedAt(d.getCreatedAt());
            adminDiscussVo.setDiscussId(discussId);
            adminDiscussVo.setContent(d.getContent());
            adminDiscussVo.setUpvoteCount(d.getUpvoteCount());
            AppCustomer appCustomer = customerMap.get(customerId);
            if(appCustomer!=null){
                String nickname = appCustomer.getNickname();
                adminDiscussVo.setNickname(nickname);
            }
            List<String> daUrlList = daMap.get(discussId);
            adminDiscussVo.setAttachementList(daUrlList);
            return adminDiscussVo;
        }).collect(Collectors.toList());

        pageVO.setTotal(discussIPage.getTotal());
        pageVO.setList(adminDiscussVoList);
        return pageVO;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void recursionRemove(Discuss discuss){
        LambdaQueryWrapper<Discuss> subDisQuery = new LambdaQueryWrapper<>();
        subDisQuery.eq(Discuss::getParentId, discuss.getId());
        List<Discuss> subDisList = baseMapper.selectList(subDisQuery);
        if(CollectionUtil.isEmpty(subDisList)){
            return;
        }

        subDisList.forEach(this::recursionRemove);

        List<Long> removeIds = subDisList.stream().map(Discuss::getId).collect(Collectors.toList());
        // 删除子评论
        baseMapper.deleteBatchIds(removeIds);

        // 删除子评论的点赞
        LambdaQueryWrapper<Upvote> upvoteQuery = new LambdaQueryWrapper<>();
        upvoteQuery.eq(Upvote::getBusinessType, BusinessTypeEnum.DISCUSS.getCode());
        upvoteQuery.in(Upvote::getBusinessId, removeIds);
        List<Upvote> upvotes = upvoteMapper.selectList(upvoteQuery);
        if(!CollectionUtil.isEmpty(upvotes)){
            List<Long> upvoteIdList = upvotes.stream().map(Upvote::getId).collect(Collectors.toList());
            upvoteMapper.deleteBatchIds(upvoteIdList);
        }
    }
}
